from flask import Flask, request
from flask_cors import CORS
import json
import requests
import threading
import time
import math
import hashlib
from datetime import datetime
import base64
import hmac
import urllib3

urllib3.disable_warnings()


class Const:
    port_number = 7002

    web_secret = "PwGoWznFta89De3JLx6pwR6M"  # 前端私钥，用于前端请求，注意修改后跟前端代码同步
    server_secret = "md2cxuZQdbRSMP0nqlWr0WaYwpeiwpWb"  # 后端私钥，用于token签发
    web_expire = 7200  # token过期时间
    jdy_secret = "8xpn1kJqrhOW3qiyo1uQxcq5"  # 简道云私钥，用于接收简道云数据推送，注意跟简道云数据推送同步

    jdy_api_key = "iVdPjkfbzfk62wKIyfkuyeeyb0IQ9m1t"
    jdy_app_id = "612ef2aeeb4b4c0008ef0213"
    jdy_account_entry_id = "612e1fa3a44c2100089420c6"
    jdy_tree_entry_id = "612e1fb00d40ec00083a2bb5"

    jdy_account = {}
    jdy_app_account_widgets = ["account", "password"]
    jdy_parent_widgets = []
    jdy_standard_widgets_name_init = [
        "name", "local_code", "local_level", "global_code", "last_date", "has_children"]
    jdy_unchangeable_widgets_name_init = [
        "local_code", "local_level", "global_code", "last_date", "has_children"]
    jdy_standard_widgets_name = []
    jdy_unchangeable_widgets_name = []


# region 简道云API预装方法
class APIUtils:
    WEBSITE = "https://www.jiandaoyun.com"
    RETRY_IF_LIMITED = True

    # 构造函数
    def __init__(self, entry_id):
        self.url_get_widgets = APIUtils.WEBSITE + '/api/v1/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/widgets'
        self.url_get_data = APIUtils.WEBSITE + '/api/v2/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/data'
        self.url_retrieve_data = APIUtils.WEBSITE + '/api/v2/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/data_retrieve'
        self.url_update_data = APIUtils.WEBSITE + '/api/v3/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/data_update'
        self.url_create_data = APIUtils.WEBSITE + '/api/v3/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/data_create'
        self.url_delete_data = APIUtils.WEBSITE + '/api/v1/app/' + \
            Const.jdy_app_id + '/entry/' + entry_id + '/data_delete'
        self.api_key = Const.jdy_api_key

    # 带有认证信息的请求头
    def get_req_header(self):
        return {
            'Authorization': 'Bearer ' + self.api_key,
            'Content-Type': 'application/json;charset=utf-8'
        }

    # 发送http请求
    def send_request(self, method, request_url, data):
        headers = self.get_req_header()
        if method == 'GET':
            res = requests.get(request_url, params=data,
                               headers=headers, verify=False)
        elif method == 'POST':
            res = requests.post(request_url, data=json.dumps(
                data), headers=headers, verify=False)
        else:
            print("请求方式错误！")
            return
        result = res.json()
        if res.status_code >= 400:
            if result['code'] == 8303 and APIUtils.RETRY_IF_LIMITED:
                # 5s后重试
                time.sleep(5)
                return self.send_request(method, request_url, data)
            else:
                raise Exception('请求错误！', result)
        else:
            return result

    # 获取表单字段
    def get_form_widgets(self):
        result = self.send_request('POST', self.url_get_widgets, {})
        return result.get("widgets")

    # 根据条件获取表单中的数据
    def get_form_data(self, data_id, limit, fields, data_filter):
        result = self.send_request('POST', self.url_get_data, {
            'data_id': data_id,
            'limit': limit,
            'fields': fields,
            'filter': data_filter
        })
        return result.get("data")

    # 获取表单中满足条件的所有数据
    def get_all_data(self, fields, data_filter):
        form_data = []

        # 递归取下一页数据
        def get_next_page(data_id):
            data = self.get_form_data(data_id, 10000, fields, data_filter)
            if data:
                for v in data:
                    form_data.append(v)
                data_id = data[len(data) - 1]['_id']
                get_next_page(data_id)

        get_next_page('')
        return form_data

    # 检索一条数据
    def retrieve_data(self, data_id):
        result = self.send_request('POST', self.url_retrieve_data, {
            'data_id': data_id
        })
        return result.get("data")

    # 创建一条数据
    def create_data(self, data):
        result = self.send_request('POST', self.url_create_data, {
            'data': data
        })
        return result.get("data")

    # 更新数据
    def update_data(self, data_id, data):
        result = self.send_request('POST', self.url_update_data, {
            'data_id': data_id,
            'data': data
        })
        return result.get("data")

    # 删除数据
    def delete_data(self, data_id):
        result = self.send_request('POST', self.url_delete_data, {
            'data_id': data_id
        })
        return result


# endregion


app = Flask(__name__,
            static_url_path='',  # 配置静态文件的访问 url 前缀
            static_folder='../jdy-sol-lab-web/dist',    # 配置静态文件的文件夹
            )
CORS(app, supports_credentials=True)

# 主页
@app.route("/")
def index_file_route():
    return app.send_static_file("index.html")

# region token签发认证方法
def generate_token(key=Const.server_secret, expire=Const.web_expire):
    """
    @Args:
    key: str (用户给定的key，需要用户保存以便之后验证token,每次产生token时的key 都可以是同一个key)
    expire: int(最大有效时间，单位为s)
    @Return:
    state: str
    """
    ts_str = str(time.time() + expire)
    ts_byte = ts_str.encode("utf-8")
    sha1_tshexstr = hmac.new(key.encode("utf-8"), ts_byte, 'sha1').hexdigest()
    token = ts_str + ':' + sha1_tshexstr
    b64_token = base64.urlsafe_b64encode(token.encode("utf-8"))
    return b64_token.decode("utf-8")


def certify_token(token, key=Const.server_secret):
    """
    @Args:
    key: str
    token: str
    @Returns:
    boolean
    """
    try:
        token_str = base64.urlsafe_b64decode(token).decode('utf-8')
    except:
        return False
    token_list = token_str.split(':')
    if len(token_list) != 2:
        return False
    ts_str = token_list[0]
    if float(ts_str) < time.time():
        # token expired
        return False
    known_sha1_tsstr = token_list[1]
    sha1 = hmac.new(key.encode("utf-8"), ts_str.encode('utf-8'), 'sha1')
    calc_sha1_tsstr = sha1.hexdigest()
    if calc_sha1_tsstr != known_sha1_tsstr:
        # token certification failed
        return False
    # token certification success
    return True


# endregion


# region 小工具
def get_now():
    return datetime.now().strftime('%Y-%m-%d %H:%M:%S')


def local2utc(local_st):
    """本地时间转UTC时间（-8:00）"""
    time_struct = time.mktime(datetime.strptime(
        local_st, '%Y-%m-%d %H:%M:%S').timetuple())
    utc_st = datetime.utcfromtimestamp(time_struct)
    return utc_st.strftime('%Y-%m-%dT%H:%M:%SZ')


def get_signature(nonce, payload, secret, timestamp):
    content = ':'.join([nonce, payload, secret, timestamp]).encode('utf-8')
    m = hashlib.sha1()
    m.update(content)
    return m.hexdigest()


def get_widgets():
    entry_id = Const.jdy_tree_entry_id
    api = APIUtils(entry_id)
    widgets = api.get_form_widgets()
    widgets_name = []
    widgets_dict = {}
    for widget in widgets:
        widgets_name.append(widget.get("name"))
        widgets_dict.setdefault(widget.get("name"), widget.get("label"))
    if not widgets_name:
        widgets_name = None
    if not widgets_dict:
        widgets_dict = None
    return {
        "names": widgets_name,
        "dicts": widgets_dict
    }


# endregion


# region api运算
def tree_cal():
    parent_lists = []  # 双亲表示法
    level_count = len(Const.jdy_parent_widgets)+1
    entry_id = Const.jdy_tree_entry_id
    api = APIUtils(entry_id)
    for i in range(level_count):
        level = i + 1
        my_filter = {
            "rel": "and",
            "cond": [{
                "field": "local_level",
                "type": "text",
                "method": "eq",
                "value": str(level)
            }]
        }
        form_data = api.get_all_data(get_widgets().get("names"), my_filter)
        parent_dicts = {}
        if form_data is not None and form_data:
            for son in form_data:
                son.pop("appId")
                son.pop("entryId")
                son.pop("_id")
                if son.get("global_code") is not None:
                    son.setdefault("title", son.get("name") +
                                   "_" + son.get("global_code"))
                    son.setdefault("children", [])
                    son.setdefault("level", str(level))
                    son.setdefault("scopedSlots",  {"title": "title"})
                    son.setdefault("disabled", False)
                    son.setdefault("selectable", True)
                    parent_dicts.setdefault(son.get("global_code"), son)
        parent_lists.append(parent_dicts)

    def find_parent(son_code, son_level):
        son_trace = parent_lists[son_level-1].get(son_code)
        if son_trace is not None:
            parent_code = son_trace.get("parent_code"+"_"+str(son_level-1))
            if parent_code is not None and son_level > 1:
                parent_trace = parent_lists[son_level-2].get(parent_code)
                if parent_trace is not None:
                    parent_lists[son_level -
                                 2][parent_code]["children"].append(son_trace)

    if level_count > 1:
        for j in range(1, level_count):
            if len(parent_lists[j]) > 0:
                for key in list(parent_lists[j].keys()):
                    find_parent(key, j+1)

    tree_answer = []
    if len(parent_lists) > 0:
        for key in list(parent_lists[0].keys()):
            tree_answer.append(parent_lists[0][key])
    if not tree_answer:
        return [503, "树不存在", None]
    return [0, "ok", tree_answer]


def node_cal(global_code, level: str):
    entry_id = Const.jdy_tree_entry_id
    if entry_id is None:
        return [503, "树不存在", None]
    api = APIUtils(entry_id)
    my_filter = {
        "rel": "and",
        "cond": [
            {
                "field": "global_code",
                "type": "text",
                "method": "eq",
                "value": global_code
            }
        ]
    }
    gw = get_widgets()
    widgets_dict = gw.get("dicts")
    if widgets_dict is None:
        return [503, "字段不存在", None]
    form_data = api.get_all_data(gw.get("names"), my_filter)
    if form_data is None or not form_data:
        return [503, "节点不存在", None]
    form_data = form_data[0]
    form_data.pop("_id")
    form_data.pop("appId")
    form_data.pop("entryId")
    node = []
    for key in list(form_data.keys()):
        node.append({
            "widget_id": key,
            "widget": widgets_dict[key],
            "value": form_data[key],
            "key": 1 if Const.jdy_standard_widgets_name.count(key) > 0 else 0,
            "changeable": 0 if Const.jdy_unchangeable_widgets_name.count(key) > 0 else 1
        })
    if not node:
        return [503, "节点不存在", None]
    return [0, "ok", node]


def children_cal(global_code, level: str):
    son_level = int(level) + 1
    son_entry_id = Const.jdy_tree_entry_id
    if son_entry_id is None:
        return [503, "树不存在", None]
    son_api = APIUtils(son_entry_id)
    son_filter = {
        "rel": "and",
        "cond": [
            {
                "field": "parent_code"+"_"+level,
                "type": "text",
                "method": "eq",
                "value": global_code
            },
            {
                "field": "local_level",
                "type": "text",
                "method": "eq",
                "value": str(son_level)
            }
        ]
    }
    gw = get_widgets()
    son_widgets_dict = gw.get("dicts")
    if son_widgets_dict is None:
        return [503, "子层级表单字段不存在", None]
    form_data = son_api.get_all_data(gw.get("names"), son_filter)
    if form_data is None or not form_data:
        return [503, "子层级表单字段不存在", None]
    children = []
    for son in form_data:
        son.pop("_id")
        son.pop("appId")
        son.pop("entryId")
        child = []
        for key in list(son.keys()):
            child.append({
                "widget_id": key,
                "widget": son_widgets_dict[key],
                "value": son[key],
            })
        children.append(child)
    if not children:
        return [503, "子层级表单字段不存在", None]
    return [0, "ok", children]


def level_cal(level: str):
    entry_id = Const.jdy_tree_entry_id
    if entry_id is None:
        return [503, "树不存在", None]
    widgets_dict = get_widgets().get("dicts")
    if widgets_dict is None:
        return [503, "树不存在", None]
    level_widgets = []
    for key in list(widgets_dict.keys()):
        level_widgets.append({
            "widget_id": key,
            "widget": widgets_dict[key],
            "changeable": 0 if (Const.jdy_unchangeable_widgets_name.count(key) > 0 or key == "local_code") else 1
            # 查询level是前端需要新增时返回的字段，此时需要告诉前端新增时允许修改local_code
        })
    if not level_widgets:
        return [503, "层级不存在", None]
    return [0, "ok", level_widgets]


def node_add(parent_code, local_code, level: str, to_add_widgets):
    p_level = str(int(level)-1)
    # 此处的level是要新增的所在层级，也即子层级
    if local_code is None or local_code == "":
        return [503, "本级代号为空", None]
    entry_id = Const.jdy_tree_entry_id
    if entry_id is None:
        return [503, "树不存在", None]
    gw = get_widgets()
    widgets_name = gw.get("names")
    if widgets_name is None:
        return [503, "没有字段", None]
    widgets_dict = gw.get("dicts")
    if widgets_dict is None:
        return [503, "没有字段", None]
    api = APIUtils(entry_id)

    # 查询本级代号是否重复
    if int(level) == 1:
        son_filter = {
            "rel": "and",
            "cond": [
                {
                    "field": "local_level",
                    "type": "text",
                    "method": "eq",
                    "value": level
                }
            ]
        }
    else:
        son_filter = {
            "rel": "and",
            "cond": [
                {
                    "field": "parent_code" + "_" + p_level,
                    "type": "text",
                    "method": "eq",
                    "value": parent_code
                },
                {
                    "field": "local_level",
                    "type": "text",
                    "method": "eq",
                    "value": level
                }
            ]
        }
    form_data = api.get_all_data(["local_code"], son_filter)
    count_repeat = 0
    for son in form_data:
        if son.get("local_code") == local_code:
            count_repeat += 1
    if count_repeat > 0:
        return [503, "本级代号重复", None]

    widgets_to_add_dict = {}
    for widget in to_add_widgets:
        name = widget.get("widget_id")
        value = widget.get("value")
        if name is not None and value is not None:
            if Const.jdy_unchangeable_widgets_name.count(name) == 0:
                widgets_to_add_dict.setdefault(name, value)
    # widgets_to_add_dict.setdefault("parent_code", parent_code)
    widgets_to_add_dict.setdefault("local_code", local_code)
    widgets_to_add_dict.setdefault("global_code", parent_code + local_code)
    widgets_to_add_dict.setdefault("last_date", local2utc(get_now()))
    widgets_to_add_dict.setdefault("local_level", level)
    widgets_to_add_dict.setdefault("has_children", "0")

    parent_filter = {
        "rel": "and",
        "cond": [
            {
                "field": "global_code",
                "type": "text",
                "method": "eq",
                "value": parent_code
            }
        ]
    }
    if int(level) >= 2:
        p_data = api.get_all_data(Const.jdy_parent_widgets, parent_filter)[0]
        for p in Const.jdy_parent_widgets:
            widgets_to_add_dict.setdefault(p, p_data.get(p))
        widgets_to_add_dict["parent_code"+"_"+p_level] = parent_code
    else:
        for p in Const.jdy_parent_widgets:
            widgets_to_add_dict.setdefault(p, "")
    add_content = {}
    for widget_name in widgets_name:
        add_content.setdefault(
            widget_name, {"value": widgets_to_add_dict.get(widget_name)})
    create_data = api.create_data(add_content)
    if create_data is None or not create_data:
        return [503, "简道云新增无响应", None]

    # 新增成功后，父节点将拥有子节点
    if int(level) >= 2:
        to_edit = [{
            "widget_id": "has_children",
            "value": "1"
        }]
        node_edit(parent_code, p_level, to_edit)

    create_data.pop("_id")
    create_data.pop("appId")
    create_data.pop("entryId")
    create_data.pop("creator")
    create_data.pop("createTime")
    create_data.pop("updateTime")
    create_data.pop("updater")
    create_data.pop("deleter")
    create_data.pop("deleteTime")
    widgets_return = []
    for key in list(create_data.keys()):
        widgets_return.append({
            "widget_id": key,
            "widget": widgets_dict.get(key),
            "value": create_data.get(key)
        })
    return [0, "ok", {
        "parent_code": parent_code,
        "global_code": create_data.get("global_code"),
        "level": level,
        "widgets": widgets_return
    }]


def node_edit(global_code, level: str, to_edit_widgets):
    if global_code is None or global_code == "":
        return [503, "全局代码为空", None]
    entry_id = Const.jdy_tree_entry_id
    if entry_id is None:
        return [503, "树不存在", None]
    gw = get_widgets()
    widgets_name = gw.get("names")
    if widgets_name is None:
        return [503, "没有字段", None]
    widgets_dict = gw.get("dicts")
    if widgets_dict is None:
        return [503, "没有字段", None]
    api = APIUtils(entry_id)
    my_filter = {
        "rel": "and",
        "cond": [{
            "field": "global_code",
            "type": "text",
            "method": "eq",
            "value": global_code
        }]
    }
    form_data = api.get_all_data(gw.get("names"), my_filter)
    if form_data is None or not form_data:
        return [503, "没有该数据", None]
    form_data = form_data[0]
    data_id = form_data.get("_id")
    widgets_to_edit_dict = {}
    for widget in to_edit_widgets:
        name = widget.get("widget_id")
        value = widget.get("value")
        if name is not None and value is not None:
            if name == "has_children" or Const.jdy_unchangeable_widgets_name.count(name) == 0:
                widgets_to_edit_dict.setdefault(name, value)
    widgets_to_edit_dict.setdefault("last_date", local2utc(get_now()))
    edit_content = {}
    for widget_name in widgets_name:
        value = widgets_to_edit_dict.get(widget_name)
        if (Const.jdy_unchangeable_widgets_name.count(widget_name) == 0 or widget_name == "last_date"
                or widget_name == "has_children") and value is not None:
            edit_content.setdefault(widget_name, {"value": value})
    update_data = api.update_data(data_id, edit_content)
    if update_data is None or not update_data:
        return [503, "简道云修改无响应", None]
    update_data.pop("_id")
    update_data.pop("appId")
    update_data.pop("entryId")
    update_data.pop("creator")
    update_data.pop("createTime")
    update_data.pop("updateTime")
    update_data.pop("updater")
    update_data.pop("deleter")
    update_data.pop("deleteTime")
    widgets_return = []
    for key in list(update_data.keys()):
        widgets_return.append({
            "widget_id": key,
            "widget": widgets_dict.get(key),
            "value": update_data.get(key)
        })
    return [0, "ok", {
        "global_code": update_data.get("global_code"),
        "level": level,
        "widgets": widgets_return
    }]


def node_delete(global_code, level: str):
    if global_code is None or global_code == "":
        return [503, "全局代码为空", "fail"]
    entry_id = Const.jdy_tree_entry_id
    if entry_id is None:
        return [503, "层级表单未建立", "fail"]
    gw = get_widgets()
    widgets_name = gw.get("names")
    if widgets_name is None:
        return [503, "没有字段", "fail"]
    widgets_dict = gw.get("dicts")
    if widgets_dict is None:
        return [503, "没有字段", "fail"]
    api = APIUtils(entry_id)
    my_filter = {
        "rel": "and",
        "cond": [{
            "field": "global_code",
            "type": "text",
            "method": "eq",
            "value": global_code
        }]
    }
    form_data = api.get_all_data(gw.get("names"), my_filter)
    if form_data is None or not form_data:
        return [503, "没有该数据", "fail"]
    form_data = form_data[0]
    data_id = form_data.get("_id")
    result = api.delete_data(data_id)

    # 遍历树删除子
    son_entry_id = entry_id
    if son_entry_id is not None:
        son_api = APIUtils(son_entry_id)
        son_filter = {
            "rel": "and",
            "cond": [
                {
                    "field": "parent_code" + "_" + level,
                    "type": "text",
                    "method": "eq",
                    "value": global_code
                }
            ]
        }
        son_form_data = son_api.get_all_data(["global_code"], son_filter)
        if son_form_data is not None and son_form_data:
            for son_data in son_form_data:
                son_id = son_data.get("_id")
                son_result = api.delete_data(son_id)

    # 查询被删除节点的父节点是否含有子节点
    if int(level) > 1:
        p_level = str(int(level)-1)
        parent_code = form_data.get("parent_code" + "_" + p_level)
        res = children_cal(parent_code, p_level)
        if res[0] != 0:
            to_edit = [{
                "widget_id": "has_children",
                "value": "0"
            }]
            node_edit(parent_code, p_level, to_edit)
    if result.get("status") != "success":
        return [503, "简道云删除无响应", "fail"]
    return [0, "ok", "success"]


# endregion


# region 简道云修改账户信息
def refresh_cache_account(data):
    Const.jdy_account = {}
    api = APIUtils(Const.jdy_account_entry_id)
    form_data = api.get_all_data(Const.jdy_app_account_widgets, {})
    for v in form_data:
        account = v.get("account")
        password = v.get("password")
        Const.jdy_account.setdefault(account, password)


def node_level_refresh(data):
    Const.jdy_parent_widgets = []
    Const.jdy_standard_widgets_name = Const.jdy_standard_widgets_name_init
    Const.jdy_unchangeable_widgets_name = Const.jdy_unchangeable_widgets_name_init
    names = get_widgets().get("names")
    for name in names:
        if "parent_code" in name:
            Const.jdy_parent_widgets.append(name)
    Const.jdy_standard_widgets_name.extend(Const.jdy_parent_widgets)
    Const.jdy_unchangeable_widgets_name.extend(Const.jdy_parent_widgets)


@app.route('/account', methods=["POST"])  # 因为简道云只接收POST请求
def change_account_receive():
    payload = request.data.decode('utf-8')
    nonce = request.args['nonce']
    timestamp = request.args['timestamp']
    if request.headers['x-jdy-signature'] != get_signature(nonce, payload, Const.jdy_secret, timestamp):
        return 'fail', 401
    threading.Thread(target=refresh_cache_account,
                     args=(json.loads(payload),)).start()
    return 'success'


@app.route('/tree', methods=["POST"])  # 因为简道云只接收POST请求
def change_tree_receive():
    payload = request.data.decode('utf-8')
    nonce = request.args['nonce']
    timestamp = request.args['timestamp']
    if request.headers['x-jdy-signature'] != get_signature(nonce, payload, Const.jdy_secret, timestamp):
        return 'fail', 401
    receive_data = json.loads(payload)
    if receive_data.get("op") != "data_create":
        threading.Thread(target=node_level_refresh,
                         args=(receive_data,)).start()
    return 'success'


# endregion


# region api接口
@app.route('/login', methods=["POST"])  # 因为简道云只接收POST请求
def login_receive():
    """
    @headers = {
        "web_signature": "17ccad7c36f03b424ccf8b6d2cbdb2a768948aa6"
    }
    @params = {
        "nonce": "17ccad7c36f03b424ccf8b6d2cbdb2a768948aa6",
        "timestamp": "1628081813187"
    }
    @payload = {
        "account": "admin",
        "password": "shubaokeji"
    }
    @return = {
        "state": 401,
        "token": "",
        "msg": "Refused"
    }
    @return = {
        "state": 402,
        "token": "",
        "msg": "Wrong Account"
    }
    @return = {
        "state": 403,
        "token": "",
        "msg": "Wrong Password"
    }
    @return = {
        "state": 0,
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "msg": "Successful"
    }
    """
    payload = json.dumps(json.loads(
        request.data.decode('utf-8')), separators=(',', ':'))
    nonce = request.args['nonce']
    timestamp = request.args['timestamp']
    if request.headers['web_signature'] != get_signature(nonce, payload, Const.web_secret, timestamp):
        return json.dumps({
            "state": 401,
            "token": "",
            "msg": "Refused"
        })
    accounting = json.loads(payload)
    check_password = Const.jdy_account.get(accounting.get("account"))
    given_password = accounting.get("password")
    if check_password is None or given_password is None:
        return json.dumps({
            "state": 402,
            "token": "",
            "msg": "Wrong Account"
        })
    encoded_password = given_password.encode(encoding='utf-8')
    md5 = hashlib.md5()
    md5.update(encoded_password)
    md5_password = md5.hexdigest().upper()
    if md5_password != check_password:
        return json.dumps({
            "state": 403,
            "token": "",
            "msg": "Wrong Password"
        })
    return json.dumps({
        "state": 0,
        "token": generate_token(),
        "msg": "Successful",
        "nonce": nonce,
        "timestamp": timestamp
    })


@app.route('/checktoken', methods=["POST"])  # 因为简道云只接收POST请求
def check_token():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA=="
    }
    @return = {
        "state": 402,
        "msg": "token_error"
    }
    @return = {
        "state": 0,
        "msg": "ok"
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error"
        })
    return json.dumps({
        "state": 0,
        "msg": "ok"
    })


@app.route('/gettree', methods=["POST"])  # 因为简道云只接收POST请求
def get_tree():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA=="
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "tree": Null
    }
    @return = {
        "state": 503,
        "msg": "树不存在",
        "tree": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "tree": {
            "title": "名称001",
            "name": "名称",
            "local_code": "001",
            "global_code": "001",
            “parent_code”: "",
            "unit": "个",
            "amount": "10",
            "lase_date": "2018-01-01T10:10:10.000Z",
            "define1": "a",
            "define2": "b",
            ...,
            "level": 1,
            "children": [
                {
                    "title": "名称001001",
                    "name": "名称",
                    "local_code": "001",
                    "global_code": "001001",
                    “parent_code”: "",
                    "unit": "个",
                    "amount": "10",
                    "lase_date": "2018-01-01T10:10:10.000Z",
                    "define1": "a",
                    "define2": "b",
                    ...,
                    "level": 2,
                    "children": [],
                    "scopedSlots":  {"title": "title"},
                    "disabled": False,
                    "selectable": True
                }
            ],
            "scopedSlots": {"title": "title"},
            "disabled": False,
            "selectable": True
        }
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "tree": None
        })
    answer = tree_cal()
    return json.dumps({
        "state": answer[0],
        "msg": answer[1],
        "tree": answer[2]
    }).encode(encoding='utf-8')


@app.route('/getnode', methods=["POST"])  # 因为简道云只接收POST请求
def get_node():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "global_code": "001",
        "level": 1
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "node": Null
    }
    @return = {
        "state": 503,
        "msg": "节点不存在",
        "tree": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "node": [
            {
                "widget_id": "name",
                "widget": "名称",
                "value": "型体A",
                "key": 1,
                "changeable": 1
            },
            {
                "widget_id": "local_code",
                "widget": "本级代号",
                "value": "001",
                "key": 1,
                "changeable": 0
            },
            {
                "widget_id": "global_code",
                "widget": "编码",
                "value": "001",
                "key": 1,
                "changeable": 0
            },
            {
                "widget_id": "parent_code",
                "widget": "上级编码",
                "value": "",
                "key": 1,
                "changeable": 0
            },
            {
                "widget_id": "unit",
                "widget": "单位",
                "value": "个",
                "key": 1,
                "changeable": 1
            },
            {
                "widget_id": "amount",
                "widget": "数量",
                "value": 1,
                "key": 1,
                "changeable": 1
            },
            {
                "widget_id": "last_date",
                "widget": "上次更新日期",
                "value": "2021-08-05T16:00:00.000Z",
                "key": 1,
                "changeable": 0
            },
            {
                "widget_id": "define1",
                "widget": "产品描述",
                "value": "a",
                "key": 0,
                "changeable": 1
            },
            {
                "widget_id": "define2",
                "widget": "规格型号",
                "value": "d",
                "key": 0,
                "changeable": 1
            }
        ]
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_global_code = payload.get("global_code")
    received_level = payload.get("level")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "node": None
        })
    answer = node_cal(received_global_code, received_level)
    return json.dumps({
        "state": answer[0],
        "msg": answer[1],
        "node": answer[2]
    }).encode(encoding='utf-8')


@app.route('/getchildren', methods=["POST"])  # 因为简道云只接收POST请求
def get_children():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "global_code": "001",
        "level": 1
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "global_code": "001",
        "children": Null
    }
    @return = {
        "state": 503,
        "msg": "子节点不存在",
        "global_code": "001",
        "children": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "global_code": "001",
        "children": [
            [
                {
                    "widget_id": "name",
                    "widget": "名称",
                    "value": "x部位"
                },
                {
                    "widget_id": "local_code",
                    "widget": "本级代号",
                    "value": "001"
                },
                {
                    "widget_id": "global_code",
                    "widget": "编码",
                    "value": "001001"
                },
                {
                    "widget_id": "parent_code",
                    "widget": "上级编码",
                    "value": "001"
                },
                {
                    "widget_id": "unit",
                    "widget": "单位",
                    "value": "个"
                },
                {
                    "widget_id": "amount",
                    "widget": "数量",
                    "value": 3
                },
                {
                    "widget_id": "last_date",
                    "widget": "上次更新日期",
                    "value": "2021-08-05T16:00:00.000Z"
                },
                {
                    "widget_id": "define1",
                    "widget": "说明",
                    "value": "qw"
                },
                {
                    "widget_id": "define2",
                    "widget": "规格型号",
                    "value": "re"
                }
            ],
            [
                {
                    "widget_id": "name",
                    "widget": "名称",
                    "value": "y部位"
                },
                {
                    "widget_id": "local_code",
                    "widget": "本级代号",
                    "value": "002"
                },
                {
                    "widget_id": "global_code",
                    "widget": "编码",
                    "value": "001002"
                },
                {
                    "widget_id": "parent_code",
                    "widget": "上级编码",
                    "value": "001"
                },
                {
                    "widget_id": "unit",
                    "widget": "单位",
                    "value": "个"
                },
                {
                    "widget_id": "amount",
                    "widget": "数量",
                    "value": 4
                },
                {
                    "widget_id": "last_date",
                    "widget": "上次更新日期",
                    "value": "2021-08-05T16:00:00.000Z"
                },
                {
                    "widget_id": "define1",
                    "widget": "说明",
                    "value": "qweq"
                },
                {
                    "widget_id": "define2",
                    "widget": "规格型号",
                    "value": "rwrq"
                }
            ]
        ]
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_global_code = payload.get("global_code")
    received_level = payload.get("level")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "global_code": received_global_code,
            "children": None
        })
    answer = children_cal(received_global_code, received_level)
    return json.dumps({
        "state": answer[0],
        "msg": answer[1],
        "global_code": received_global_code,
        "children": answer[2]
    }).encode(encoding='utf-8')


@app.route('/getlevel', methods=["POST"])  # 因为简道云只接收POST请求
def get_level():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "level": 1
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "level": Null
    }
    @return = {
        "state": 503,
        "msg": "层级不存在",
        "level": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "level": [
            {
                "widget_id": "name",
                "widget": "名称",
                "changeable": 1
            },
            {
                "widget_id": "local_code",
                "widget": "本级代号",
                "changeable": 0
            },
            {
                "widget_id": "global_code",
                "widget": "编码",
                "changeable": 0
            },
            {
                "widget_id": "parent_code",
                "widget": "上级编码",
                "changeable": 0
            },
            {
                "widget_id": "unit",
                "widget": "单位",
                "changeable": 1
            },
            {
                "widget_id": "amount",
                "widget": "数量",
                "changeable": 1
            },
            {
                "widget_id": "last_date",
                "widget": "上次更新日期",
                "changeable": 0
            },
            {
                "widget_id": "define1",
                "widget": "产品描述",
                "changeable": 1
            },
            {
                "widget_id": "define2",
                "widget": "规格型号",
                "changeable": 1
            }
        ]
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_level = payload.get("level")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "level": None
        })
    answer = level_cal(received_level)
    return json.dumps({
        "state": answer[0],
        "msg": answer[1],
        "level": answer[2]
    }).encode(encoding='utf-8')


@app.route('/addnode', methods=["POST"])  # 因为简道云只接收POST请求
def add_node():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "parent_code": "001",
        "local_code": "010",
        "level": 2,
        "widgets": [
            {
                "widget_id": "name",
                "widget": "名称",
                "value": "x部位"
            },
            {
                "widget_id": "unit",
                "widget": "单位",
                "value": "个"
            },
            {
                "widget_id": "amount",
                "widget": "数量",
                "value": 3
            },
            {
                "widget_id": "define1",
                "widget": "说明",
                "value": "qw"
            },
            {
                "widget_id": "define2",
                "widget": "规格型号",
                "value": "re"
            } # ,
            # ...
        ]
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "node": Null
    }
    @return = {
        "state": 503,
        "msg": "error_msg",
        "node": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "node": {
            "parent_code": "001",
            "global_code": "001010",
            "level": 2,
            "widgets": [
                {
                    "widget_id": "name",
                    "widget": "名称",
                    "value": "test"
                },
                {
                    "widget_id": "local_code",
                    "widget": "本级代号",
                    "value": "010"
                },
                {
                    "widget_id": "global_code",
                    "widget": "编码",
                    "value": "001010"
                },
                {
                    "widget_id": "parent_code",
                    "widget": "上级编码",
                    "value": "001"
                },
                {
                    "widget_id": "unit",
                    "widget": "单位",
                    "value": "个"
                },
                {
                    "widget_id": "amount",
                    "widget": "数量",
                    "value": 3
                },
                {
                    "widget_id": "last_date",
                    "widget": "上次更新日期",
                    "value": "2021-08-09T17:55:58.000Z"
                },
                {
                    "widget_id": "define1",
                    "widget": "说明",
                    "value": "qw"
                },
                {
                    "widget_id": "define2",
                    "widget": "规格型号",
                    "value": "re"
                }
            ]
        }
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_parent_code = payload.get("parent_code")
    received_local_code = payload.get("local_code")
    received_level = payload.get("level")
    received_widgets = payload.get("widgets")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "node": None
        })
    result = node_add(received_parent_code, received_local_code,
                      received_level, received_widgets)
    return json.dumps({
        "state": result[0],
        "msg": result[1],
        "node": result[2]
    }).encode(encoding='utf-8')


@app.route('/editnode', methods=["POST"])  # 因为简道云只接收POST请求
def edit_node():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "global_code": "001010",
        "level": 2,
        "widgets": [
            {
                "widget_id": "name",
                "widget": "名称",
                "value": "x部位"
            },
            {
                "widget_id": "unit",
                "widget": "单位",
                "value": "个"
            },
            {
                "widget_id": "amount",
                "widget": "数量",
                "value": 3
            },
            {
                "widget_id": "define1",
                "widget": "说明",
                "value": "qw"
            },
            {
                "widget_id": "define2",
                "widget": "规格型号",
                "value": "re"
            } # ,
            # ...
        ]
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "node": Null
    }
    @return = {
        "state": 503,
        "msg": "error_msg",
        "node": Null
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "node": {
            "global_code": "001001",
            "level": 2,
            "widgets": [
                {
                    "widget_id": "name",
                    "widget": "名称",
                    "value": "x部位x"
                },
                {
                    "widget_id": "local_code",
                    "widget": "本级代号",
                    "value": "001"
                },
                {
                    "widget_id": "global_code",
                    "widget": "编码",
                    "value": "001001"
                },
                {
                    "widget_id": "parent_code",
                    "widget": "上级编码",
                    "value": "001"
                },
                {
                    "widget_id": "unit",
                    "widget": "单位",
                    "value": "个ge"
                },
                {
                    "widget_id": "amount",
                    "widget": "数量",
                    "value": 4
                },
                {
                    "widget_id": "last_date",
                    "widget": "上次更新日期",
                    "value": "2021-08-09T19:41:10.000Z"
                },
                {
                    "widget_id": "define1",
                    "widget": "说明",
                    "value": "q1w"
                },
                {
                    "widget_id": "define2",
                    "widget": "规格型号",
                    "value": "r2e"
                }
            ]
        }
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_global_code = payload.get("global_code")
    received_level = payload.get("level")
    received_widgets = payload.get("widgets")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "node": None
        })
    result = node_edit(received_global_code, received_level, received_widgets)
    return json.dumps({
        "state": result[0],
        "msg": result[1],
        "node": result[2]
    }).encode(encoding='utf-8')


@app.route('/deletenode', methods=["POST"])  # 因为简道云只接收POST请求
def delete_node():
    """
    @payload = {
        "token": "MTYyODA5OTk1MC41NjM4MDQ6OGZkMDY5MWFjMzJiMmUwNWZkZDI0NmYyMzQzZTlkYzA2OWFZmY4NA==",
        "global_code": "001010",
        "level": 2
    }
    @return = {
        "state": 402,
        "msg": "token_error",
        "node": "fail"
    }
    @return = {
        "state": 503,
        "msg": "error_msg",
        "node": "fail"
    }
    @return = {
        "state": 0,
        "msg": "ok",
        "node": "success"
    }
    """
    payload = json.loads(request.data.decode('utf-8'))
    received_token = payload.get("token")
    received_global_code = payload.get("global_code")
    received_level = payload.get("level")
    if not certify_token(received_token):
        return json.dumps({
            "state": 402,
            "msg": "token_error",
            "result": "fail"
        })
    result = node_delete(received_global_code, received_level)
    return json.dumps({
        "state": result[0],
        "msg": result[1],
        "result": result[2]
    }).encode(encoding='utf-8')


# endregion


if __name__ == '__main__':
    refresh_cache_account(None)
    node_level_refresh(None)
    app.run(host='0.0.0.0', port=Const.port_number)
